import cv2
from flask import Flask, jsonify, send_file, request, send_from_directory
from flask_cors import CORS
import os
import json
from datetime import datetime
from pathlib import Path
import threading
from typing import Dict, List, Any
import psutil
import traceback

'''
修改 src/core/stream_manager.py
在 __init__ 方法中添加：
    # Web服务器
    self.web_server = None
在 start 方法中添加（在其他组件启动后）：
    # 启动Web服务器
    from src.services.web_server import WebServer
    self.web_server = WebServer(self.config_loader, self)
    self.web_server.start()
在 stop 方法中添加：
    # 停止Web服务器
    if self.web_server:
        self.web_server.stop()
依赖
    # Web服务
    flask>=2.3.3
    flask-cors>=4.0.0
    psutil>=5.9.5
'''


class WebServer:
    def __init__(self, config_loader, stream_manager=None, port=9999):
        self.config = config_loader.get_app_config()  # 获取配置
        self.stream_manager = stream_manager            # 流管理器
        self.port = port                                # 端口

        # 视频存储路径
        self.video_path = Path(self.config['recording']['output_path'])
        self.video_path.mkdir(parents=True, exist_ok=True)

        # 创建Flask应用
        self.app = Flask(__name__)
        CORS(self.app)  # 允许跨域

        # 服务器状态
        self.server_start_time = datetime.now()
        self.is_running = False
        self.server_thread = None

        # 设置路由
        self._setup_routes()

    def _setup_routes(self):
        """设置API路由"""

        @self.app.route('/api/status', methods=['GET'])
        def get_status():
            """获取服务器状态"""
            try:
                # 获取视频列表
                videos = self._get_video_list()
                total_size = sum(video['size'] for video in videos)

                # 获取系统状态
                status = {
                    'server_running': self.is_running,
                    'stream_manager_running': self.stream_manager.is_running if self.stream_manager else False,
                    'detector_initialized': self.stream_manager.detector.is_initialized if self.stream_manager else False,
                    'recording_active': self.stream_manager.recorder.is_recording if self.stream_manager else False,
                    'start_time': self.server_start_time.isoformat(),
                    'uptime_seconds': (datetime.now() - self.server_start_time).total_seconds(),
                    'video_count': len(videos),
                    'total_size_mb': round(total_size / (1024 * 1024), 2),
                    'disk_usage': self._get_disk_usage(),
                    'rtsp_input': self.config['rtsp']['input_url'],
                    # 修改为显示带认证的观看URL
                    'rtsp_output': self.config['rtsp'].get('view_url', self.config['rtsp']['output_url']),
                    'rtsp_credentials': self.config['rtsp'].get('view_credentials', {})
                }

                # 添加处理统计
                if self.stream_manager:
                    status.update({
                        'total_frames_processed': getattr(self.stream_manager, 'total_frames_processed', 0),
                        'total_dangers_detected': getattr(self.stream_manager, 'total_dangers_detected', 0),
                        'current_fps': self._get_current_fps()
                    })

                return jsonify({'success': True, 'data': status})
            except Exception as e:
                return jsonify({'success': False, 'error': str(e)}), 500

        @self.app.route('/api/videos', methods=['GET'])
        def get_videos():
            """获取视频列表"""
            try:
                videos = self._get_video_list()
                return jsonify({'success': True, 'data': videos})
            except Exception as e:
                return jsonify({'success': False, 'error': str(e)}), 500

        @self.app.route('/api/video/<path:filename>', methods=['GET'])
        def get_video(filename):
            """获取视频文件（完整返回，不支持范围请求）"""
            try:
                # 1. 构建视频路径
                base_dir = Path(__file__).parent.parent.parent  # 项目根目录
                video_dir = base_dir / "data" / "output_results"
                video_path = video_dir / filename

                print(f"正在请求视频文件: {video_path}")

                # 2. 安全检查
                if not video_path.exists():
                    print(f"文件不存在: {video_path}")
                    return jsonify({'success': False, 'error': '视频不存在'}), 404

                if not video_path.suffix.lower() == '.mp4':
                    print(f"不支持的文件格式: {video_path.suffix}")
                    return jsonify({'success': False, 'error': '仅支持MP4格式'}), 400

                # 3. 防止目录遍历攻击
                try:
                    video_path.relative_to(video_dir)
                except ValueError:
                    print(f"非法路径访问尝试: {filename}")
                    return jsonify({'success': False, 'error': '非法访问'}), 403

                # 4. 直接返回整个视频文件
                return send_file(
                    str(video_path),
                    mimetype='video/mp4',
                    as_attachment=False,
                    conditional=False
                )

            except Exception as e:
                print(f"视频请求错误: {str(e)}")
                return jsonify({'success': False, 'error': str(e)}), 500

        @self.app.route('/api/video/<filename>', methods=['DELETE'])
        def delete_video(filename):
            """删除视频文件"""
            try:
                video_file = self.video_path / filename
                if video_file.exists() and video_file.suffix == '.mp4':
                    os.remove(video_file)
                    return jsonify({'success': True, 'message': '视频已删除'})
                else:
                    return jsonify({'success': False, 'error': '视频不存在'}), 404
            except Exception as e:
                return jsonify({'success': False, 'error': str(e)}), 500

        @self.app.route('/api/videos/clear', methods=['POST'])
        def clear_videos():
            """清空所有视频"""
            try:
                count = 0
                for video_file in self.video_path.glob('*.mp4'):
                    os.remove(video_file)
                    count += 1
                return jsonify({'success': True, 'message': f'已删除 {count} 个视频'})
            except Exception as e:
                return jsonify({'success': False, 'error': str(e)}), 500

        @self.app.route('/')
        def index():
            """返回前端页面"""
            return self._get_frontend_html()

    def _get_video_list(self) -> List[Dict[str, Any]]:
        """获取视频列表信息"""
        videos = []
        for video_file in sorted(self.video_path.glob('*.mp4'), key=lambda x: x.stat().st_mtime, reverse=True):
            stat = video_file.stat()

            # 解析文件名获取时间
            # 假设文件名格式: danger_20231225_143022.mp4
            filename = video_file.name
            try:
                time_str = filename.replace('danger_', '').replace('.mp4', '')
                record_time = datetime.strptime(time_str, '%Y%m%d_%H%M%S')
            except:
                record_time = datetime.fromtimestamp(stat.st_mtime)

            videos.append({
                'filename': filename,
                'size': stat.st_size,
                'size_mb': round(stat.st_size / (1024 * 1024), 2),
                'created_time': record_time.isoformat(),
                'duration': self._get_video_duration(video_file),
                'url': f'/api/video/{filename}'
            })

        return videos

    def _get_video_duration(self, video_path: Path) -> float:
        """获取视频时长（秒）"""
        try:
            cap = cv2.VideoCapture(str(video_path))
            fps = cap.get(cv2.CAP_PROP_FPS)
            frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
            cap.release()

            if fps > 0:
                return round(frame_count / fps, 1)
        except:
            pass
        return 0

    def _get_disk_usage(self) -> Dict[str, Any]:
        """获取磁盘使用情况"""
        try:
            disk = psutil.disk_usage(str(self.video_path))
            return {
                'total_gb': round(disk.total / (1024 ** 3), 2),
                'used_gb': round(disk.used / (1024 ** 3), 2),
                'free_gb': round(disk.free / (1024 ** 3), 2),
                'percent': round(disk.percent, 1)
            }
        except:
            return {'total_gb': 0, 'used_gb': 0, 'free_gb': 0, 'percent': 0}

    def _get_current_fps(self) -> float:
        """获取当前FPS"""
        if self.stream_manager and hasattr(self.stream_manager, 'frame_times'):
            from src.utils.frame_utils import FrameUtils
            return FrameUtils.calculate_fps(self.stream_manager.frame_times)
        return 0

    def _get_frontend_html(self) -> str:
        """返回前端HTML页面"""
        return '''
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>RTSP YOLOv8 监控系统</title>
  <style>
    body { margin: 0; font-family: Arial, sans-serif; background: #f5f5f5; }
    .container { max-width: 1400px; margin: 0 auto; padding: 20px; }
    .status-card { background: white; border-radius: 8px; padding: 20px; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
    .status-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-top: 15px; }
    .status-item { text-align: center; padding: 15px; background: #f8f9fa; border-radius: 6px; }
    .status-value { font-size: 24px; font-weight: bold; color: #409EFF; }
    .status-label { color: #666; font-size: 14px; margin-top: 5px; }
    .video-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; }
    .video-card { background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.1); transition: transform 0.2s; }
    .video-card:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.15); }
    .video-info { padding: 15px; }
    .status-indicator { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 5px; }
    .status-online { background: #67C23A; }
    .status-offline { background: #F56C6C; }
    .status-recording { background: #E6A23C; animation: pulse 1.5s infinite; }
    @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; } }
    .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
    .btn { padding: 6px 12px; font-size: 14px; cursor: pointer; border-radius: 4px; border: none; }
    .btn.primary { background-color: #409EFF; color: white; }
    .btn.danger { background-color: #F56C6C; color: white; }
    .btn.small { font-size: 12px; padding: 4px 8px; }
    .tag { display: inline-block; padding: 4px 8px; font-size: 12px; background-color: #f0f0f0; color: #666; border-radius: 4px; margin-right: 5px; }
    .video-player { width: 100%; max-width: 800px; }
    dialog::backdrop { background: rgba(0, 0, 0, 0.5); }
  </style>
</head>
<body>
  <div class="container">
    <div class="header">
      <h1>RTSP YOLOv8 监控系统</h1>
      <button class="btn primary" id="refreshBtn">🔄 刷新</button>
    </div>

    <!-- 系统状态 -->
    <div class="status-card">
      <h2>
        系统状态
        <span id="status-indicator" class="status-indicator"></span>
        <span id="status-text" style="font-size: 14px; color: #666;"></span>
        <span id="recording-indicator" class="status-indicator status-recording" style="margin-left: 10px; display:none;"></span>
        <span id="recording-text" style="font-size: 14px; color: #E6A23C; display:none;">录制中</span>
      </h2>

      <div class="status-grid" id="statusGrid"></div>

      <div style="margin-top: 15px;" id="tagsContainer"></div>
    </div>

    <!-- 视频列表 -->
    <div class="status-card">
      <div style="display: flex; justify-content: space-between; align-items: center;">
        <h2>录制视频 (<span id="videoCount">0</span>)</h2>
        <button class="btn danger small" id="clearBtn" style="display: none;">清空所有视频</button>
      </div>

      <div id="emptyMessage" style="text-align: center; padding: 40px; color: #999;">暂无录制视频</div>

      <div class="video-grid" id="videoList"></div>
    </div>
  </div>

  <dialog id="videoDialog">
    <h3>视频播放</h3>
    <video id="videoPlayer" class="video-player" controls autoplay></video>
    <div style="text-align: right; margin-top: 10px;">
      <button class="btn primary" onclick="document.getElementById('videoDialog').close()">关闭</button>
    </div>
  </dialog>

  <script>
    const statusGrid = document.getElementById('statusGrid');
    const videoList = document.getElementById('videoList');
    const emptyMessage = document.getElementById('emptyMessage');
    const videoCountSpan = document.getElementById('videoCount');
    const clearBtn = document.getElementById('clearBtn');

    const statusIndicator = document.getElementById('status-indicator');
    const statusText = document.getElementById('status-text');
    const recordingIndicator = document.getElementById('recording-indicator');
    const recordingText = document.getElementById('recording-text');
    const tagsContainer = document.getElementById('tagsContainer');

    document.getElementById('refreshBtn').addEventListener('click', loadData);
    clearBtn.addEventListener('click', clearAllVideos);

    async function loadData() {
      try {
        const [statusRes, videosRes] = await Promise.all([
          fetch('/api/status').then(res => res.json()),
          fetch('/api/videos').then(res => res.json())
        ]);

        if (statusRes.success) {
          renderStatus(statusRes.data);
        }

        if (videosRes.success) {
          renderVideos(videosRes.data);
        }
      } catch (e) {
        alert('获取数据失败：' + e.message);
      }
    }

    function renderStatus(data) {
      statusIndicator.className = "status-indicator " + (data.server_running ? "status-online" : "status-offline");
      statusText.textContent = data.server_running ? "运行中" : "已停止";

      if (data.recording_active) {
        recordingIndicator.style.display = 'inline-block';
        recordingText.style.display = 'inline-block';
      } else {
        recordingIndicator.style.display = 'none';
        recordingText.style.display = 'none';
      }

      const stats = [
        { label: '录制视频', value: data.video_count },
        { label: '占用空间', value: `${data.total_size_mb} MB` },
        { label: '当前FPS', value: (data.current_fps || 0).toFixed(1) },
        { label: '危险检测次数', value: data.total_dangers_detected || 0 },
        { label: '运行时长', value: formatUptime(data.uptime_seconds) },
        { label: '剩余空间', value: `${data.disk_usage?.free_gb || 0} GB` }
      ];

      statusGrid.innerHTML = '';
      stats.forEach(stat => {
        const div = document.createElement('div');
        div.className = 'status-item';
        div.innerHTML = `<div class="status-value">${stat.value}</div><div class="status-label">${stat.label}</div>`;
        statusGrid.appendChild(div);
      });

      tagsContainer.innerHTML = '';
      const inputTag = document.createElement('span');
      inputTag.className = 'tag';
      inputTag.textContent = `输入: ${data.rtsp_input}`;
      tagsContainer.appendChild(inputTag);

      const outputTag = document.createElement('span');
      outputTag.className = 'tag';
      outputTag.style.backgroundColor = '#dff0d8';
      outputTag.textContent = `观看: ${data.rtsp_output}`;
      if (data.rtsp_credentials?.username) {
        outputTag.innerHTML += ` (用户名: ${data.rtsp_credentials.username})`;
      }
      tagsContainer.appendChild(outputTag);
    }

    function renderVideos(videos) {
      videoList.innerHTML = '';
      videoCountSpan.textContent = videos.length;
      clearBtn.style.display = videos.length > 0 ? 'inline-block' : 'none';
      emptyMessage.style.display = videos.length === 0 ? 'block' : 'none';

      videos.forEach(video => {
        const card = document.createElement('div');
        card.className = 'video-card';
        card.innerHTML = `
          <div class="video-info">
            <h4 style="margin-bottom: 10px;">${video.filename}</h4>
            <div style="color: #666; font-size: 14px;">
              <div>大小: ${video.size_mb} MB</div>
              <div>时长: ${video.duration} 秒</div>
              <div>时间: ${formatTime(video.created_time)}</div>
            </div>
            <div style="margin-top: 10px;">
              <button class="btn primary small" onclick='playVideo(${JSON.stringify(video)})'>▶ 播放</button>
              <button class="btn danger small" onclick='deleteVideo("${video.filename}")'>🗑 删除</button>
            </div>
          </div>`;
        videoList.appendChild(card);
      });
    }

    function playVideo(video) {
      const dialog = document.getElementById('videoDialog');
      const player = document.getElementById('videoPlayer');
      player.src = video.url;
      dialog.showModal();
    }

    async function deleteVideo(filename) {
      if (confirm(`确定删除视频 ${filename} 吗？`)) {
        try {
          const res = await fetch(`/api/video/${filename}`, { method: 'DELETE' });
          const result = await res.json();
          if (result.success) {
            alert('删除成功');
            loadData();
          } else {
            alert('删除失败: ' + result.error);
          }
        } catch (e) {
          alert('请求失败: ' + e.message);
        }
      }
    }

    async function clearAllVideos() {
      if (confirm('确定要清空所有视频吗？此操作不可恢复！')) {
        try {
          const res = await fetch('/api/videos/clear', { method: 'POST' });
          const result = await res.json();
          if (result.success) {
            alert(result.message);
            loadData();
          } else {
            alert('操作失败: ' + result.error);
          }
        } catch (e) {
          alert('请求失败: ' + e.message);
        }
      }
    }

    function formatTime(isoStr) {
      const date = new Date(isoStr);
      return date.toLocaleString('zh-CN');
    }

    function formatUptime(seconds) {
      if (!seconds) return '0秒';
      const h = Math.floor(seconds / 3600);
      const m = Math.floor((seconds % 3600) / 60);
      const s = Math.floor(seconds % 60);
      return h > 0 ? `${h}小时${m}分` : (m > 0 ? `${m}分${s}秒` : `${s}秒`);
    }

    // 自动刷新
    setInterval(loadData, 5000);
    loadData();
  </script>
</body>
</html>
'''

    def start(self):
        """启动Web服务器"""
        self.is_running = True
        self.server_thread = threading.Thread(
            target=lambda: self.app.run(
                host='0.0.0.0',
                port=self.port,
                debug=False,
                use_reloader=False
            )
        )
        self.server_thread.daemon = True
        self.server_thread.start()
        print(f"Web服务器已启动: http://localhost:{self.port}")

    def stop(self):
        """停止Web服务器"""
        self.is_running = False
        # Flask没有优雅的停止方法，这里只是标记状态
        print("Web服务器已停止")
